package pl.umk.mat.fred.unicore.urc.storagemanagement.actions;

import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import javax.security.auth.x500.X500Principal;

import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.util.URIUtil;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.unigrids.services.atomic.types.ProtocolType;
import org.w3.x2005.x08.addressing.EndpointReferenceType;

import de.fzj.unicore.rcp.identity.IdentityActivator;
import de.fzj.unicore.uas.client.StorageClient;
import de.fzj.unicore.uas.client.TransferControllerClient;
import de.fzj.unicore.uas.security.IUASSecurityProperties;

import pl.umk.mat.fred.unicore.urc.storagemanagement.actions.helpers.SubprogressMonitorHelper;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tools.StorageTools;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tree.nodes.BelowStorageNode;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tree.nodes.CatalogNode;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tree.nodes.FileNode;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tree.nodes.GridNode;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tree.nodes.MultiplyCatalogNode;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tree.nodes.Node;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tree.nodes.StorageNode;
import pl.umk.mat.fred.unicore.urc.storagemanagement.unicore.Storage;
import pl.umk.mat.fred.unicore.urc.storagemanagement.views.StorageBrowserView;

public class PasteAction extends Action {

	private StorageBrowserView view;
	
	public PasteAction(StorageBrowserView view) {
		this.view = view;
		this.setText("Paste");
	}
	
	public void run() {
		if (view.getClipboard() != null) {
			final IStructuredSelection currentSelection = (IStructuredSelection) view.getTreeViewer().getSelection();
			final Object dstElement = currentSelection.getFirstElement(); 
			Storage storage = null;
			String path = null;
			
			if (dstElement instanceof GridNode) {
				GridNode node = (GridNode) dstElement;
				storage = randomStorage(node);
				path = node.getPath();
			} else if (dstElement instanceof StorageNode) {
				StorageNode node = (StorageNode) dstElement;
				storage = node.getStorage();
				path = node.getPath();
			} else if (dstElement instanceof CatalogNode) {
				CatalogNode node = (CatalogNode) dstElement;
				storage = node.getStorage();
				path = node.getPath();
			} else if (currentSelection.getFirstElement() instanceof MultiplyCatalogNode) {
				MultiplyCatalogNode node = (MultiplyCatalogNode) dstElement;
				storage = randomStorage(node.getCatalogs());
				path = node.getPath();
			}
			
			final Storage storage2 = storage;
			final String path2 = path;
			
			Job job = new Job("Transfer files") {
				@Override
				protected IStatus run(IProgressMonitor monitor) {
					try {
						Map<TransferControllerClient, SubprogressMonitorHelper> tccMonitorMap = new HashMap<TransferControllerClient, SubprogressMonitorHelper>();
						monitor.beginTask("Transfer to " + storage2.getName(),  view.getClipboard().size() * 10);
						System.out.println("Monitor size " + view.getClipboard().size() * 100);
						for (Object n : view.getClipboard().toList()) {
							try {
								SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, 100);
								TransferControllerClient tcc = copyNode((BelowStorageNode) n, storage2, path2);
								SubprogressMonitorHelper helper = new SubprogressMonitorHelper(monitor);
								// sometimes totalWork is -1, then we need to wait
								long totalWork = tcc.getSize();
								for (int i = 0; i < 10 && totalWork < 0 ; i++) {
									Thread.sleep(100);
									totalWork = tcc.getSize();
								}
								int move = 0;
								while (totalWork > 511) {
									System.out.println("CurrentTotal " + totalWork);
									totalWork = totalWork >> 1;
									move++;
								}
								helper.setMove(move);
								System.out.println("Move " + move);
								System.out.println("Fred " + totalWork * Math.pow(2, move));
								subMonitor.beginTask("a", (int) totalWork);
								System.out.println("subMonitor " + totalWork);
								tccMonitorMap.put(tcc, helper);
							} catch (URIException e) {
								e.printStackTrace();
							} catch (Exception e) {
								e.printStackTrace();
							}
						}
						
						while (!tccMonitorMap.isEmpty()) {
							Iterator<Entry<TransferControllerClient, SubprogressMonitorHelper>> itr = tccMonitorMap.entrySet().iterator();
							while (itr.hasNext()) {
								Entry<TransferControllerClient, SubprogressMonitorHelper> entry = itr.next();
								TransferControllerClient tcc = entry.getKey();
								SubprogressMonitorHelper helper = entry.getValue();
								IProgressMonitor subMonitor = helper.getMonitor();
								if (monitor.isCanceled()) {
									break;
								}
								
								if (tcc.isComplete()) {	
									itr.remove();
									subMonitor.done();
									try {
										tcc.destroy();
									} catch (Exception e) {
										e.printStackTrace();
									}
								} else {
									long transfered = tcc.getTransferredBytes() >> helper.getMove();
									System.out.println("Transfered " + transfered);
									int progress = (int) (transfered - helper.getWorked());
									if (progress > 0) {
										subMonitor.worked(progress);
										System.out.println("worked " + progress);
										helper.setWorked((int)transfered);
									}
								}
							}
							if (monitor.isCanceled()) {
								for (Iterator<Entry<TransferControllerClient, SubprogressMonitorHelper>> itr2 = tccMonitorMap.entrySet().iterator(); itr2.hasNext();) {
									Entry<TransferControllerClient, SubprogressMonitorHelper> entry = itr2.next();
									try {
										entry.getKey().destroy();
									} catch (Exception e) {
									}
								}
								return org.eclipse.core.runtime.Status.CANCEL_STATUS;
							}
							try {
								Thread.sleep(50);
							} catch (InterruptedException e) {
							}
						}
					} finally {
						view.getDoubleClickAction().run((Node) dstElement);
						monitor.done();
					}
					return org.eclipse.core.runtime.Status.OK_STATUS;
				}
			};
			job.schedule();
			
		}
	}
	
	private TransferControllerClient copyNode(BelowStorageNode toCopy, Storage dstStorage, String dstPath) throws Exception {
		System.out.println("toCopy " + toCopy.getPath());
		System.out.println("storage " + dstStorage.getName());
		System.out.println("destination " + dstPath);
		System.out.println("------------------------");
		
		TransferControllerClient tcc = null;
		Storage srcStorage = toCopy.getStorage();
		String fileName = toCopy.getPath();
		StorageClient dstSMS = null;
		//String encryptedDstPath = URIUtil.encode(dstPath, org.apache.commons.httpclient.URI.allowed_within_path);
		
		if (toCopy instanceof CatalogNode) {
			CatalogNode catalogNode = (CatalogNode) toCopy;
			dstPath += "/" + catalogNode.getName();
		}
		
		String encryptedDstPath = dstPath;
		EndpointReferenceType dstEPR = dstStorage.getClient().getEPR();
					           
		String dstDN = dstStorage.getDN();
		if (dstDN == null) {
			throw new IllegalStateException("DN for destination Storage is unavailable");
		}
		IUASSecurityProperties securityProperties = IdentityActivator.getDefault().getUASSecProps(dstEPR.getAddress().getStringValue());
		securityProperties.getETDSettings().setReceiver(new X500Principal(dstDN));		           
		securityProperties.getETDSettings().setExtendTrustDelegation(true);
			
		dstSMS = new StorageClient(dstEPR, securityProperties);
		long fileSize = (toCopy instanceof FileNode ? ((FileNode)toCopy).getFileSize() : 1025);
		
		String protocol = StorageTools.getProtocol(fileSize, dstStorage.getSupportedProtocols(), srcStorage.getSupportedProtocols()).toString();
		Map<String, String> extraParameters = new HashMap<String, String>();
		String srcU6URI = protocol + ":" + srcStorage.getUrl() + "#" + URLEncoder.encode(fileName, "ASCII");
		srcU6URI = URIUtil.encode(srcU6URI, org.apache.commons.httpclient.URI.allowed_within_path);
		return dstSMS.fetchFile(srcU6URI, encryptedDstPath, extraParameters);
	}

	private Storage randomStorage(List<CatalogNode> catalogs) {
		if (catalogs.size() < 1) {
			throw new IllegalStateException("Empty list of catalogs");
		}
		Random random = new Random();
		int r = random.nextInt(catalogs.size());
		return catalogs.get(r).getStorage();
	}
	
	private Storage randomStorage(GridNode gridNode) {
		Random random = new Random();
		List<Node> storages = gridNode.getChildren();
		if (storages.size() < 1) {
			throw new IllegalStateException("There are no storages in GridNode");
		}
		int r = random.nextInt(storages.size());
		return ((StorageNode) gridNode.getChildren().get(r)).getStorage();
	}
}
